package com.tarena.lbs.article.web.repository;

import com.alibaba.fastjson2.JSON;
import com.tarena.lbs.pojo.content.entity.ActionSearchEntity;
import com.tarena.lbs.pojo.content.entity.ArticleSearchEntity;
import com.tarena.lbs.pojo.content.query.ArticleQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Repository
@Slf4j
public class ArticleRepository {
    //无需手动配置RestHighLevelClient 依赖spring-boot-starter-data-elasticsearch的时候
    //这个客户端就在springboot帮助我们创建
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private ArticleESRepository esRepository;
    @Autowired
    private ActionESRepository actionESRepository;
    @Autowired
    private StringRedisTemplate redisTemplate;//类型是RedisTemplate的子类
    public List<ArticleSearchEntity> searchArticles(ArticleQuery repoQuery) {
        //入参query 任何一个属性都可能是非空的 拼接bool子条件
        //比如文章标题就是查询条件 `海底捞` 使用字段属性 到es搜索 使用rangeQuery termQuery geoDistance
        //matchQuery repoQuery对象中,每一个非空属性都要使用不同的es的query对象.最终组织到一起查询
        //明显这里的搜索查询 是复合查询 拼接多个子条件 子条件依据非空数据拼接
        //1.创建查询搜索的请求对象
        SearchRequest request=new SearchRequest("lbs_article");
        SearchSourceBuilder builder=new SearchSourceBuilder();
        //2.构造一个布尔条件
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        //根据repoQuery 业务非空属性 拼接boolquery的字条件
        if (repoQuery.getArticleTitle()!=null&&repoQuery.getArticleTitle().length()>0){
            //matchQuery 使用索引中文档字段articleTitle
            query.must(QueryBuilders.matchQuery("articleTitle",repoQuery.getArticleTitle()));
        }
        //articleCategoryId非空 termQuery搜索相等逻辑
        if (repoQuery.getArticleCategoryId()!=null){
            query.must(QueryBuilders.termQuery("articleCategoryId",repoQuery.getArticleCategoryId()));
        }
        //articleLabel非空 termQuery搜索相等逻辑
        if (repoQuery.getArticleLabel()!=null&&repoQuery.getArticleLabel().length()>0){
            query.must(QueryBuilders.termQuery("articleLabel",repoQuery.getArticleLabel()));
        }
        //文章状态只有可能传递 一个元素 0 1 termQuery
        if (repoQuery.getArticleStatus()!=null&&repoQuery.getArticleStatus().size()>0){
            //list中只有可能是1个值
            query.must(QueryBuilders.termQuery("articleStatus",repoQuery.getArticleStatus().get(0)));
        }
        //source来源 termQuery
        if (repoQuery.getSource()!=null){
            query.must(QueryBuilders.termQuery("source",repoQuery.getSource()));
        }
        //activityId 活动id termQuery
        if (repoQuery.getActivityId()!=null){
            query.must(QueryBuilders.termQuery("activityId",repoQuery.getActivityId()));
        }
        //latitude纬度,longitude经度 非空表示手机定位 只有一个没用
        if(StringUtils.isNotEmpty(repoQuery.getLatitude())&&StringUtils.isNotEmpty(repoQuery.getLongitude())){
            //定义使用这个手机定位 查询中心点 检查范围200公里
            GeoDistanceQueryBuilder geoQuery = QueryBuilders.geoDistanceQuery("location");
            geoQuery.point(Double.valueOf(repoQuery.getLatitude()),Double.valueOf(repoQuery.getLongitude()));
            geoQuery.distance(200, DistanceUnit.KILOMETERS);
            query.must(geoQuery);
        }
        //updateStartTime 范围查询的下限rangeQuery
        if (repoQuery.getUpdateStartTime()!=null){
            query.must(QueryBuilders.rangeQuery("updateTime").gte(repoQuery.getUpdateStartTime()));
        }
        //updateEndTime 上限
        if (repoQuery.getUpdateEndTime()!=null){
            query.must(QueryBuilders.rangeQuery("updateTime").lte(repoQuery.getUpdateEndTime()));
        }
        //userId termQuery
        if (repoQuery.getUserId()!=null){
            query.must(QueryBuilders.termQuery("userId",repoQuery.getUserId()));
        }
        builder.query(query);
        request.source(builder);
        //打印builder 仔细阅读本次查询搜索构造bool条件
        log.info("本次分页列表查询,bool条件:{}",builder);
        List<ArticleSearchEntity> entities=null;
        //3.发起请求
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            //文档集合hits非空时候 才转化entities json转化成entity
            if (hits!=null&& hits.length>0){
                //命中了一批文档数据
                entities=Arrays.stream(hits).map(hit->{
                    //hit拿到sourceString 转化成entity
                    String sourceAsString = hit.getSourceAsString();
                    return JSON.parseObject(sourceAsString,ArticleSearchEntity.class);
                }).collect(Collectors.toList());
            }
        } catch (IOException e) {
            log.error("文章分页列表查询,异常",e);
        }
        return entities;
    }

    public void save(ArticleSearchEntity entity) {
        esRepository.save(entity);
    }

    public List<ArticleSearchEntity> getArticleLabels(ArticleQuery aQuery) {
        //使用RestHighLevelClient计算查询
        //1.准备搜索对象
        SearchRequest request = new SearchRequest("lbs_article");
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //2.提供查询条件 geoDistanceQuery,termQuery(status=1)避免查询到草稿文章 构造bool
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        //2.1 geo字条件
        GeoPoint center=new GeoPoint(Double.valueOf(aQuery.getLatitude()),Double.valueOf(aQuery.getLongitude()));
        GeoDistanceQueryBuilder query1 =
                QueryBuilders.geoDistanceQuery("location")
                        .point(center)
                        .distance(100l, DistanceUnit.KILOMETERS);
        //2.2查询status状态=1的数据
        TermQueryBuilder query2 = QueryBuilders.termQuery("articleStatus", 1);
        query.must(query1);
        query.must(query2);
        builder.query(query);
        //2.3查询去重 基于articleLabel
        builder.collapse(new CollapseBuilder("articleLabel"));
        request.source(builder);
        log.info("查询条件:{}",builder);
        //4.发起请求 解析结果 封装entities返回
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            log.info("查询到命中文档:{}",hits);
            if (hits!=null&& hits.length>0){
                return Arrays.stream(hits).map(hit->{
                    String sourceAsString = hit.getSourceAsString();
                    return JSON.parseObject(sourceAsString,ArticleSearchEntity.class);
                }).collect(Collectors.toList());
            }
        }catch (Exception e){
            log.error("查询统计文章标签集合失败",e);
        }
        return null;
    }

    public ArticleSearchEntity getById(Integer id) {
        //restHighLevelClient 使用id查询文档较复杂
        Optional<ArticleSearchEntity> response = esRepository.findById(id);
        //从返回数据解析 entity实体类
        ArticleSearchEntity entity = response.get();
        return entity;
    }

    public void updateCountandSaveAction(Integer articleId,Integer behave,Integer time,Integer userId) {
        //使用文章id 更新文章accessCount likeCount favoriteCount
        //1点赞 2收藏 3评论
        ArticleSearchEntity articleEntity = esRepository.findById(articleId).get();
        //更新redis中记录的数据
        String countKey="areticle:count:"+articleId;
        //拿到hash处理对象 泛型一定要是String否则无法计步
        HashOperations<String,String,String> opsHash = redisTemplate.opsForHash();
        //不用判断 使用countKey 自增 behave就可以了
        opsHash.increment(countKey,behave+"",1);
        //记录用户行为
        ActionSearchEntity actionSearchEntity=new ActionSearchEntity();
        //给这个action对象的属性一个一个封装
        actionSearchEntity.setArticleType(articleEntity.getArticleCategoryId()+"");
        actionSearchEntity.setArticleId(articleId);
        actionSearchEntity.setArticleTitle(articleEntity.getArticleTitle());
        actionSearchEntity.setArticleLabel(articleEntity.getArticleLabel());
        actionSearchEntity.setBehavior(behave);
        actionSearchEntity.setArticleUserId(articleEntity.getUserId());
        actionSearchEntity.setBehaviorUserId(userId);
        actionSearchEntity.setCreateTime(new Date());
        actionESRepository.save(actionSearchEntity);
    }

    public List<ActionSearchEntity> selectActionArticles(Integer userId, Integer behave) {
        //restHighLevelClient 通过构造bool条件查询 userId and behave 对结果去重的返回
        SearchRequest request = new SearchRequest("lbs_action");
        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("behaviorUserId",userId));
        query.must(QueryBuilders.termQuery("behavior",behave));
        builder.query(query);
        //每个用户 对一篇文章可能记录N条数据 要去重处理
        builder.collapse(new CollapseBuilder("articleId"));
        request.source(builder);
        //发起请求 解析响应
        List<ActionSearchEntity> entities=null;
        try{
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            if (hits!=null&& hits.length>0){
                entities=Arrays.stream(hits).map(hit->{
                    String sourceAsString = hit.getSourceAsString();
                    return JSON.parseObject(sourceAsString,ActionSearchEntity.class);
                }).collect(Collectors.toList());
            }
        }catch (Exception e){
            log.error("查询用户行为文章失败",e);
        }
        return entities;
    }

    public ArticleSearchEntity getArticleById(Integer id) {
        //1.读取ES的文章
        ArticleSearchEntity entity = esRepository.findById(id).get();
        //2.读取redis的count值
        String countKey="areticle:count:"+id;
        HashOperations<String,String,String> opsHash = redisTemplate.opsForHash();
        //当没人访问这个文章 没人点赞收藏 redis数据是空的
        String likeCount = opsHash.get(countKey, 1+"");//点赞 有可能是空;
        String favoriteCount = opsHash.get(countKey, 2+"");
        String accessCount = opsHash.get(countKey, 3+"");
        entity.setLikeCount(likeCount==null?0:Integer.valueOf(likeCount));
        entity.setFavoriteCount(favoriteCount==null?0:Integer.valueOf(favoriteCount));
        entity.setAccessCount(accessCount==null?0:Integer.valueOf(accessCount));
        return entity;
    }
}
